// Magic Software, Inc.
// http://www.magic-software.com
// Copyright (c) 2000, All Rights Reserved
//
// Source code from Magic Software is supplied under the terms of a license
// agreement and may not be copied or disclosed except in accordance with the
// terms of that agreement.  The various license agreements may be found at
// the Magic Software web site.  This file is subject to the license
//
// RESTRICTED USE SOURCE CODE
// http://www.magic-software.com/License/restricted.pdf

#include "MgcBezierCylinder.h"
#include "MgcBezierMesh.h"
#include "MgcTriMesh.h"

MgcImplementRTTI(MgcBezierMesh,MgcGeometry);
MgcImplementStream(MgcBezierMesh);

//----------------------------------------------------------------------------
MgcBezierMesh::MgcBezierMesh (unsigned int uiCtrlQuantity,
    MgcVector3* akCtrlVertex, bool bUseNormals, MgcColor* akCtrlColor,
    MgcVector2* akCtrlTexture, unsigned int uiPatchQuantity,
    MgcBezierPatchPtr* aspkPatch)
    :
    MgcGeometry(uiCtrlQuantity,akCtrlVertex,0,akCtrlColor,akCtrlTexture)
{
    m_uiPatchQuantity = uiPatchQuantity;
    m_aspkPatch = aspkPatch;
    m_uiTrianglePatchQuantity = 0;
    m_uiRectanglePatchQuantity = 0;
    m_uiCylinderPatchQuantity = 0;
    for (unsigned int uiI = 0; uiI < m_uiPatchQuantity; uiI++)
    {
        MgcBezierPatch::PatchType eType = m_aspkPatch[uiI]->GetType();
        if ( eType == MgcBezierPatch::PT_TRIANGLE )
            m_uiTrianglePatchQuantity++;
        else if ( eType == MgcBezierPatch::PT_RECTANGLE )
            m_uiRectanglePatchQuantity++;
        else
            m_uiCylinderPatchQuantity++;
    }

    // allocate space for trimesh (default level = 0)
    unsigned int uiVertexQuantity, uiTriangleQuantity;
    GetMeshQuantities(0,uiVertexQuantity,uiTriangleQuantity);

    MgcVector3* akVertex = new MgcVector3[uiVertexQuantity];

    MgcVector3* akNormal;
    if ( bUseNormals )
        akNormal = new MgcVector3[uiVertexQuantity];
    else
        akNormal = 0;

    MgcColor* akColor;
    if ( akCtrlColor )
        akColor = new MgcColor[uiVertexQuantity];
    else
        akColor = 0;

    MgcVector2* akTexture;
    if ( akCtrlTexture )
        akTexture = new MgcVector2[uiVertexQuantity];
    else
        akTexture = 0;

    unsigned int* auiConnect = new unsigned int[3*uiTriangleQuantity];

    m_pkMesh = new MgcTriMesh(uiVertexQuantity,akVertex,akNormal,akColor,
        akTexture,uiTriangleQuantity,auiConnect);

    Tessellate(0);
}
//----------------------------------------------------------------------------
MgcBezierMesh::MgcBezierMesh ()
{
    m_uiPatchQuantity = 0;
    m_uiTrianglePatchQuantity = 0;
    m_uiRectanglePatchQuantity = 0;
    m_uiCylinderPatchQuantity = 0;
    m_aspkPatch = 0;
    m_pkMesh = 0;
    m_uiLevel = 0;
}
//----------------------------------------------------------------------------
MgcBezierMesh::~MgcBezierMesh ()
{
    if ( m_aspkPatch )
    {
        for (unsigned int uiI = 0; uiI < m_uiPatchQuantity; uiI++)
            m_aspkPatch[uiI] = 0;
        delete[] m_aspkPatch;
    }

    delete m_pkMesh;
}
//----------------------------------------------------------------------------
void MgcBezierMesh::UpdateModelNormals ()
{
    // TO DO.  Implement?  Control point normals don't make sense here.
}
//----------------------------------------------------------------------------
void MgcBezierMesh::UpdateRenderState (MgcRenderState* apkState[])
{
    // MgcGeometry base never gets drawn, it is just a repository for control
    // points.  Transfer the render state to the trimesh that is drawn.
    m_pkMesh->UpdateRenderState(apkState);
}
//----------------------------------------------------------------------------
void MgcBezierMesh::Draw (MgcRenderer& rkRenderer)
{
    // MgcGeometry base never gets drawn, it is just a repository for control
    // points.  Draw the trimesh instead.
    m_pkMesh->Draw(rkRenderer);
}
//----------------------------------------------------------------------------
void MgcBezierMesh::GetMeshQuantities (unsigned int uiLevel,
    unsigned int& uiVertexQuantity, unsigned int& uiTriangleQuantity)
{
    // compute number of vertices and triangles needed for trimesh
    uiVertexQuantity = 0;
    uiTriangleQuantity = 0;

    if ( m_uiTrianglePatchQuantity > 0 )
    {
        uiVertexQuantity +=
            MgcBezierPatch::GetVerticesPerTrianglePatch(uiLevel) *
            m_uiTrianglePatchQuantity;

        uiTriangleQuantity +=
            MgcBezierPatch::GetTrianglesPerTrianglePatch(uiLevel) *
            m_uiTrianglePatchQuantity;
    }

    if ( m_uiRectanglePatchQuantity > 0 )
    {
        uiVertexQuantity +=
            MgcBezierPatch::GetVerticesPerRectanglePatch(uiLevel) *
            m_uiRectanglePatchQuantity;

        uiTriangleQuantity +=
            MgcBezierPatch::GetTrianglesPerRectanglePatch(uiLevel) *
            m_uiRectanglePatchQuantity;
    }

    if ( m_uiCylinderPatchQuantity > 0 )
    {
        for (unsigned int uiI = 0; uiI < m_uiPatchQuantity; uiI++)
        {
            if ( m_aspkPatch[uiI]->GetType() == MgcBezierPatch::PT_CYLINDER )
            {
                MgcBezierCylinder* pkCyln =
                    MgcSmartPointerCast(MgcBezierCylinder,m_aspkPatch[uiI]);

                uiVertexQuantity +=
                    MgcBezierPatch::GetVerticesPerCylinderPatch(uiLevel,
                    pkCyln->CylinderLevel());

                uiTriangleQuantity +=
                    MgcBezierPatch::GetTrianglesPerCylinderPatch(uiLevel,
                    pkCyln->CylinderLevel());
            }
        }
    }
}
//----------------------------------------------------------------------------
void MgcBezierMesh::Tessellate (unsigned int uiLevel)
{
    m_uiLevel = uiLevel;

    // reallocate space for trimesh
    unsigned int uiVertexQuantity, uiTriangleQuantity;
    GetMeshQuantities(uiLevel,uiVertexQuantity,uiTriangleQuantity);
    m_pkMesh->Reconstruct(uiVertexQuantity,uiTriangleQuantity);

    // fill in the trimesh with tessellated patch triangles
    uiVertexQuantity = 0;
    uiTriangleQuantity = 0;
    for (unsigned int uiI = 0; uiI < m_uiPatchQuantity; uiI++)
    {
        m_aspkPatch[uiI]->Tessellate(uiLevel,Vertices(),Colors(),Textures(),
            m_pkMesh,uiVertexQuantity,uiTriangleQuantity);
    }
}
//----------------------------------------------------------------------------

//---------------------------------------------------------------------------
// streaming
//---------------------------------------------------------------------------
MgcObject* MgcBezierMesh::Factory (MgcStream& rkStream)
{
    MgcBezierMesh* pkObject = new MgcBezierMesh;
    MgcStream::Link* pkLink = new MgcStream::Link(pkObject);
    pkObject->Load(rkStream,pkLink);
    return pkObject;
}
//---------------------------------------------------------------------------
void MgcBezierMesh::Load (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcGeometry::Load(rkStream,pkLink);

    // native data
    MgcStreamRead(rkStream,m_uiPatchQuantity);
    MgcStreamRead(rkStream,m_uiTrianglePatchQuantity);
    MgcStreamRead(rkStream,m_uiRectanglePatchQuantity);
    MgcStreamRead(rkStream,m_uiCylinderPatchQuantity);
    MgcStreamRead(rkStream,m_uiLevel);

    // link data
    for (unsigned int uiP = 0; uiP < m_uiPatchQuantity; uiP++)
    {
        MgcBezierPatch* pkPatch;
        MgcStreamRead(rkStream,pkPatch);
        pkLink->Add(pkPatch);
    }

    MgcTriMesh* pkMesh;
    MgcStreamRead(rkStream,pkMesh);
    pkLink->Add(pkMesh);
}
//---------------------------------------------------------------------------
void MgcBezierMesh::Link (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcGeometry::Link(rkStream,pkLink);

    m_aspkPatch = new MgcBezierPatchPtr[m_uiPatchQuantity];

    MgcObject* pkLinkID;
    for (unsigned int uiP = 0; uiP < m_uiPatchQuantity; uiP++)
    {
        pkLinkID = pkLink->GetLinkID();
        m_aspkPatch[uiP] = (MgcBezierPatch*) rkStream.GetFromMap(pkLinkID);
    }

    pkLinkID = pkLink->GetLinkID();
    m_pkMesh = (MgcTriMesh*) rkStream.GetFromMap(pkLinkID);
}
//---------------------------------------------------------------------------
bool MgcBezierMesh::Register (MgcStream& rkStream)
{
    if ( !MgcGeometry::Register(rkStream) )
        return false;

    for (unsigned int uiP = 0; uiP < m_uiPatchQuantity; uiP++)
        m_aspkPatch[uiP]->Register(rkStream);

    m_pkMesh->Register(rkStream);

    return true;
}
//---------------------------------------------------------------------------
void MgcBezierMesh::Save (MgcStream& rkStream)
{
    MgcGeometry::Save(rkStream);

    // native data
    MgcStreamWrite(rkStream,m_uiPatchQuantity);
    MgcStreamWrite(rkStream,m_uiTrianglePatchQuantity);
    MgcStreamWrite(rkStream,m_uiRectanglePatchQuantity);
    MgcStreamWrite(rkStream,m_uiCylinderPatchQuantity);
    MgcStreamWrite(rkStream,m_uiLevel);

    // link data
    for (unsigned int uiP = 0; uiP < m_uiPatchQuantity; uiP++)
        MgcStreamWrite(rkStream,m_aspkPatch[uiP]);

    MgcStreamWrite(rkStream,m_pkMesh);
}
//---------------------------------------------------------------------------
